package cn.com.libertymutual.core.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipInputStream;

import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import com.google.common.base.Strings;
import com.jcraft.jzlib.DeflaterOutputStream;
import com.jcraft.jzlib.InflaterInputStream;

/**
 * 用于制作zip压缩包
 * 
 * @author liufang
 * 
 */
public class Zipper {
	private static final Logger log = LoggerFactory.getLogger(Zipper.class);

	/**
	 * 制作压缩包
	 * @update 2016-05-19 16:34 zhaoyu
	 * 
	 * @throws Exception 
	 */
	public static void zip(OutputStream out, List<FileEntry> fileEntrys,
			String encoding) throws Exception {
		new Zipper(out, fileEntrys, encoding);
	}

	/**
	 * 制作压缩包
	 * 
	 * @update 2016-05-19 16:34 zhaoyu
	 * 
	 * @throws Exception 
	 */
	public static void zip(OutputStream out, List<FileEntry> fileEntrys) throws Exception {
		new Zipper(out, fileEntrys, null);
	}

	/**
	 * 创建Zipper对象
	 * 
	 * @param out
	 *            输出流
	 * @param filter
	 *            文件过滤，不过滤可以为null。
	 * @param srcFilename
	 *            源文件名。可以有多个源文件，如果源文件是目录，那么所有子目录都将被包含。
	 *            
	 * @update 2016-05-19 16:34 zhaoyu
	 * 
	 * @throws Exception
	 * 
	 */
	protected Zipper(OutputStream out, List<FileEntry> fileEntrys,
			String encoding) throws Exception{
		Assert.notEmpty(fileEntrys);
		long begin = System.currentTimeMillis();
		log.debug("开始制作压缩包");
		
			try {
				zipOut = new ZipOutputStream(out);
				if (!Strings.isNullOrEmpty(encoding)) {
					log.debug("using encoding: {}", encoding);
					zipOut.setEncoding(encoding);
				} else {
					log.debug("using default encoding");
				}
				for (FileEntry fe : fileEntrys) {
					zip(fe.getFile(), fe.getFilter(), fe.getZipEntry(), fe
							.getPrefix());
				}
			} finally {
				
				try {
					zipOut.close();
				} catch (IOException e) {
					log.error(e.getMessage());
				}
			}

		long end = System.currentTimeMillis();
		log.info("制作压缩包成功。耗时：{}ms。", end - begin);
	}

	/**
	 * 压缩文件
	 * 
	 * @param srcFile
	 *            源文件
	 * @param pentry
	 *            父ZipEntry
	 * @throws IOException
	 */
	private void zip(File srcFile, FilenameFilter filter, ZipEntry pentry,
			String prefix) throws IOException {
		ZipEntry entry;
		if (srcFile.isDirectory()) {
			if (pentry == null) {
				entry = new ZipEntry(srcFile.getName());
			} else {
				entry = new ZipEntry(pentry.getName() + "/" + srcFile.getName());
			}
			File[] files = srcFile.listFiles(filter);
			if( files != null )
				for (File f : files) {
					zip(f, filter, entry, prefix);
				}
		} else {
			if (pentry == null) {
				entry = new ZipEntry(prefix + srcFile.getName());
			} else {
				entry = new ZipEntry(pentry.getName() + "/" + prefix
						+ srcFile.getName());
			}
			FileInputStream in;
			log.debug("读取文件：{}", srcFile.getAbsolutePath());
			in = new FileInputStream(srcFile);
			try {
				zipOut.putNextEntry(entry);
				int len;
				while ((len = in.read(buf)) > 0) {
					zipOut.write(buf, 0, len);
				}
				zipOut.closeEntry();
			} finally {
				in.close();
			}
		}
	}

	private byte[] buf = new byte[1024];
	private ZipOutputStream zipOut;

	public static class FileEntry {
		private FilenameFilter filter;
		private String parent;
		private File file;
		private String prefix;

		public FileEntry(String parent, String prefix, File file,
				FilenameFilter filter) {
			this.parent = parent;
			this.prefix = prefix;
			this.file = file;
			this.filter = filter;
		}

		public FileEntry(String parent, File file) {
			this.parent = parent;
			this.file = file;
		}

		public FileEntry(String parent, String prefix, File file) {
			this(parent, prefix, file, null);
		}

		public ZipEntry getZipEntry() {
			if (Strings.isNullOrEmpty(parent)) {
				return null;
			} else {
				return new ZipEntry(parent);
			}
		}

		public FilenameFilter getFilter() {
			return filter;
		}

		public void setFilter(FilenameFilter filter) {
			this.filter = filter;
		}

		public String getParent() {
			return parent;
		}

		public void setParent(String parent) {
			this.parent = parent;
		}

		public File getFile() {
			return file;
		}

		public void setFile(File file) {
			this.file = file;
		}

		public String getPrefix() {
			if (prefix == null) {
				return "";
			} else {
				return prefix;
			}
		}

		public void setPrefix(String prefix) {
			this.prefix = prefix;
		}
	}
	
/**
 * 
 * @update 2016-05-19 16:26 zhaoyu
 * 
 * @throws Exception
 * 
 * */
	public static String gzip( String primStr ) throws Exception {
		if( Strings.isNullOrEmpty(primStr) ) return primStr;
		
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		GZIPOutputStream gzip = null;
		
		try {
			gzip = new GZIPOutputStream(out);
			gzip.write(primStr.getBytes());
			
			gzip.close(); gzip = null;

			return new sun.misc.BASE64Encoder().encodeBuffer(out.toByteArray());
		
		}finally {
			if( gzip != null ) {
				try {
					gzip.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if( out != null ) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
		
	}
	/**
	 * 
	 * @update 2016-05-19 16:26 zhaoyu
	 * 
	 * @throws Exception
	 * 
	 * */
	public static String ungzip( String compressedStr ) throws Exception{
		if( compressedStr == null ) return null;
		
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		ByteArrayInputStream in = null;
		GZIPInputStream ginzip = null;
		
		byte[] compressedBytes = null;
		
		try {
			compressedBytes = new sun.misc.BASE64Decoder().decodeBuffer(compressedStr);
			in = new ByteArrayInputStream(compressedBytes);
			ginzip = new GZIPInputStream(in);
			
			byte[] buffer = new byte[1024];
			int offset = -1;
			while( (offset = ginzip.read(buffer)) != -1 ) {
				out.write(buffer, 0, offset);
			}

			return out.toString();
		}finally {
			if( ginzip != null ) {
				try {
					ginzip.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			if( in != null ) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			if( out != null ) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
			}
	
	/**
	 * 
	 * @update 2016-05-19 16:26 zhaoyu
	 * 
	 * @throws Exception
	 * 
	 * */
	public static String zip( String primStr ) throws Exception{
		if( Strings.isNullOrEmpty(primStr) ) return primStr;
		
		byte compressed[] = null;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		ZipOutputStream zout = null;
		
		try {
			out = new ByteArrayOutputStream();
			zout = new ZipOutputStream(out);
			zout.putNextEntry(new ZipEntry("0"));
			zout.write( primStr.getBytes() );
			zout.closeEntry();
			
			compressed = out.toByteArray();
			
			return new sun.misc.BASE64Encoder().encodeBuffer(compressed);

		}finally {
			if( zout != null ) {
				try {
					zout.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if( out != null ) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
	}

	/**
	 * 
	 * @update 2016-05-19 16:26 zhaoyu
	 * 
	 * @throws Exception
	 * 
	 * */
	public static String unzip( String compressedStr ) throws Exception{
		if( compressedStr == null ) return null;
		
		ByteArrayOutputStream out = null;
		ByteArrayInputStream in = null;
		ZipInputStream zin = null;
		
		byte[] compressedBytes = null;
		
		try {
			compressedBytes = new sun.misc.BASE64Decoder().decodeBuffer(compressedStr);
			out = new ByteArrayOutputStream();
			in = new ByteArrayInputStream(compressedBytes);
			zin = new ZipInputStream(in);
			zin.getNextEntry();
			
			byte[] buffer = new byte[1024];
			int offset = -1;
			while( (offset = zin.read(buffer)) != -1 ) {
				out.write(buffer, 0, offset);
			}

			return out.toString();

		}finally {
			if( zin != null ) {
				try {
					zin.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			if( in != null ) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			if( out != null ) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
	}
	
	
	 /** 
	 * 压缩数据 
	 *  
	 * @param object 
	 * @return 
	 * 
	 *  
	 * 
	 * @update 2016-05-19 16:26 zhaoyu
	 * 
	 * @throws Exception
	 * 
	 * */
	 
	public static byte[] jzlib(byte[] object) throws Exception{  
		byte[] data = null;
		ByteArrayOutputStream out = null;
		DeflaterOutputStream zOut = null;
		DataOutputStream objOut = null;
		try {  
			out = new ByteArrayOutputStream();  
			///DeflaterOutputStream zOut = new DeflaterOutputStream(out, JZlib.Z_DEFAULT_COMPRESSION);
			zOut = new DeflaterOutputStream(out);
			objOut = new DataOutputStream(zOut);  
			
			objOut.write(object);  
			objOut.flush();  
			
			zOut.close();
			zOut = null;

			data = out.toByteArray();  
			  
		}finally {
			if( objOut != null ) {
				try {
					objOut.close();
				} catch (IOException e) {
				}
			}
			if( zOut != null ) {
				try {
					zOut.close();
				} catch (IOException e) {
				}
			}
			if( out != null )
				try {
					out.close();
				} catch (IOException e) {
				}
		}
		return data;  
	}

	/** 
	* 解压被压缩的数据 
	*  
	* @param object 
	* @return 
	* @throws Exception
	*/  
	public static byte[] unjzlib(byte[] object) throws Exception{  
		byte[] data = null;  
		  
			ByteArrayInputStream in = new ByteArrayInputStream(object);  
			InflaterInputStream zIn = new InflaterInputStream(in);  
			byte[] buf = new byte[1024];  
			int num = -1;  
			ByteArrayOutputStream baos = new ByteArrayOutputStream();  
			while ((num = zIn.read(buf, 0, buf.length)) != -1) {  
				baos.write(buf, 0, num);  
			}  
			data = baos.toByteArray();  
			baos.flush();  
			baos.close();  
			zIn.close();  
			in.close();  
		  
		
		return data;  
	}  
	
}
